package com.apicloud.scandevicemodule;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;

import org.apache.commons.codec.binary.Hex;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

import com.uzmap.pkg.uzcore.UZWebView;
import com.uzmap.pkg.uzcore.annotation.UzJavascriptMethod;
import com.uzmap.pkg.uzcore.uzmodule.UZModule;
import com.uzmap.pkg.uzcore.uzmodule.UZModuleContext;
import com.xtremeprog.sdk.ble.BleGattCharacteristic;
import com.xtremeprog.sdk.ble.BleGattService;
import com.xtremeprog.sdk.ble.BleRequest.FailReason;
import com.xtremeprog.sdk.ble.BleService;
import com.xtremeprog.sdk.ble.IBle;

public class ScanDeviceModule extends UZModule {

	private final String LIST_NAME = "NAME";
	private final String LIST_UUID = "UUID";
	private final String CharaData = "CharaData";
	private final int SUCCESS = 0;
	private final int STOP = 1;
	// private final int FAILD = 2;
	private static final int REQUEST_ENABLE_BT = 1;
	private static final long SCAN_PERIOD = 10000;
	private boolean showToast = false;
	private boolean notifyNow = false;

	private BleService mService;
	private IBle mBle;
	private Handler mHandler;
	private UZModuleContext uZModuleContext;
	private UZModuleContext connectedModuleContext;
	private UZModuleContext disConnectedModuleContext;
	private UZModuleContext readModuleContext;
	private UZModuleContext writeModuleContext;
	private UZModuleContext notifyModuleContext;
	private UZModuleContext stopNotifyModuleContext;
	private UZModuleContext mc;
	private JSONArray devices = new JSONArray();

	private JSONObject data = new JSONObject();
	private Set<String> listConnStatus = new HashSet<String>();
	// private String mzAddressStr;
	private BleGattCharacteristic mCharacteristic;

	public ScanDeviceModule(UZWebView webView) {
		super(webView);
		mHandler = new Handler();
		Intent bindIntent = new Intent(getContext(), BleService.class);
		getContext().bindService(bindIntent, mServiceConnection,
				Context.BIND_AUTO_CREATE);
		getContext().registerReceiver(mBleReceiver,
				BleService.getIntentFilter());

		// Ensures Bluetooth is enabled on the device. If Bluetooth is not
		// currently enabled,
		// fire an intent to display a dialog asking the user to grant
		// permission to enable it.

		if (mBle != null && !mBle.adapterEnabled()) {
			Intent enableBtIntent = new Intent(
					BluetoothAdapter.ACTION_REQUEST_ENABLE);
			startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
		}
	}

	private final ServiceConnection mServiceConnection = new ServiceConnection() {
		@Override
		public void onServiceConnected(ComponentName className,
				IBinder rawBinder) {
			mService = ((BleService.LocalBinder) rawBinder).getService();
			mBle = mService.getBle();
			if (mBle != null && !mBle.adapterEnabled()) {
				// mBle.
			}
		}

		@Override
		public void onServiceDisconnected(ComponentName classname) {
			mService = null;
		}
	};

	private final BroadcastReceiver mBleReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			// Log.e("action",
			// intent.getExtras().getString(BleService.EXTRA_ADDR)
			// + ":::" + action);
			// Set<String> entries = intent.getExtras().keySet();
			// Iterator<String> iterator = entries.iterator();
			// while (iterator.hasNext()) {
			// Log.e("Extras key", iterator.next());
			// }
			try {

				if (BleService.BLE_NOT_SUPPORTED.equals(action)) {
					runOnUiThread(new Runnable() {
						@Override
						public void run() {
							if (uZModuleContext != null) {
								JSONObject obj = new JSONObject();
								try {
									obj.put("status", STOP);
									obj.put("msg", "系统不支持你的蓝牙模块，请联系客服解决这个问题");
									uZModuleContext.success(obj, false);
								} catch (JSONException e) {

									e.printStackTrace();
								}
							}
							makeText("Ble not support");
						}
					});
				} else if (BleService.BLE_DEVICE_FOUND.equals(action)) {

					// 查找设备
					Bundle extras = intent.getExtras();
					final BluetoothDevice device = extras
							.getParcelable(BleService.EXTRA_DEVICE);
					// byte[] b =
					// extras.getByteArray(BleService.EXTRA_SCAN_RECORD);

					// Log.e("data",
					// new String(Hex.encodeHex(extras
					// .getByteArray(BleService.EXTRA_SCAN_RECORD)))+device.getAddress());
					runOnUiThread(new Runnable() {
						@Override
						public void run() {
							if (uZModuleContext != null) {
								JSONObject obj = new JSONObject();
								try {
									obj.put("status", SUCCESS);
									obj.put("name", device.getName());
									obj.put("addr", device.getAddress());
									obj.put("bondState", device.getBondState());
									makeText(obj.toString());
									uZModuleContext.success(obj, false);
								} catch (JSONException e) {

									e.printStackTrace();
								}
							}

						}
					});
				} else if (BleService.BLE_NO_BT_ADAPTER.equals(action)) {
					runOnUiThread(new Runnable() {
						@Override
						public void run() {

							makeText("No bluetooth adapter");
						}
					});
				} else if (BleService.BLE_GATT_CONNECTED.equals(action)) {
					makeText("连接成功");
				} else if (BleService.BLE_GATT_DISCONNECTED.equals(action)) {
					String addr = intent.getExtras().getString(
							BleService.EXTRA_ADDR);
					Log.e("disconn", addr);
					if (connectedModuleContext != null) {
						// 连接失败

						makeText("连接失败");
						JSONObject obj = new JSONObject();

						try {
							listConnStatus.remove(addr);
						} catch (Exception e) {

						}

						try {
							obj.put("status", STOP);
							obj.put("addr", addr);
							connectedModuleContext.success(obj, false);
						} catch (JSONException e) {

							e.printStackTrace();
						}
					}
					if (disConnectedModuleContext != null) {
						// 关闭连接
						try {
							listConnStatus.remove(addr);
						} catch (Exception e) {

						}
						makeText("关闭连接");
						JSONObject obj = new JSONObject();
						try {
							obj.put("status", STOP);
						} catch (JSONException e) {

							e.printStackTrace();
						}
						disConnectedModuleContext.success(obj, false);
					}
				} else if (BleService.BLE_SERVICE_DISCOVERED.equals(action)) {
					// 连接信息
					String addr = intent.getExtras().getString(
							BleService.EXTRA_ADDR);
					if (!TextUtils.isEmpty(addr)) {
						ArrayList<HashMap<String, Object>> list = displayGattServices(mBle
								.getServices(addr));
						if (list.size() > 0) {
							if (connectedModuleContext != null) {
								// 连接成功
								JSONObject obj = new JSONObject();
								JSONArray objArray = new JSONArray(list);
								for (int i = 0; i < devices.length(); i++) {
									try {
										if (devices.getJSONObject(i)
												.getString("addr").equals(addr)) {
											listConnStatus.add(addr);
										}
									} catch (JSONException e) {

									}

								}
								try {
									obj.put("status", SUCCESS);
									obj.put("addr", addr);
									obj.put("list", objArray);
									// Log.e("xxxx", addr +
									// listConnStatus.size());
									connectedModuleContext.success(obj, false);
								} catch (JSONException e) {

									e.printStackTrace();
								}

							}
						}

					}

				} else if (BleService.BLE_CHARACTERISTIC_READ.equals(action)
						|| BleService.BLE_CHARACTERISTIC_CHANGED.equals(action)) {
					Bundle extras = intent.getExtras();
					byte[] val = extras.getByteArray(BleService.EXTRA_VALUE);

					makeText(new String(val) + "===="
							+ ("0x" + new String(Hex.encodeHex(val))));
					Log.e("read", new String(Hex.encodeHex(val)));
					String addr = intent.getExtras().getString(
							BleService.EXTRA_ADDR);
					if (readModuleContext != null) {
						JSONObject obj = new JSONObject();
						try {
							obj.put("status", SUCCESS);
							obj.put("ascall", new String(val));
							obj.put("addr", addr);
							obj.put("hex", "0x"
									+ new String(Hex.encodeHex(val)));
							readModuleContext.success(obj, false);
						} catch (JSONException e) {

							e.printStackTrace();
						}
					}
					if (notifyModuleContext != null) {
						JSONObject obj = new JSONObject();
						try {

							// obj.put("ascall", new String(val));
							obj.put("hex", "0x"
									+ new String(Hex.encodeHex(val)));
							// Log.e("notifydata",
							// extras.getString(BleService.EXTRA_ADDR)+"="+new
							// String(Hex.encodeHex(val)));
							obj.put("addr",
									extras.getString(BleService.EXTRA_ADDR));

							if (notifyNow) {
								obj.put("status", SUCCESS);
								notifyModuleContext.success(obj, false);
							} else {
								if (listConnStatus.size() == devices.length()) {
									String id = "";
									for (int i = 0; i < devices.length(); i++) {
										JSONObject jo = devices
												.getJSONObject(i);
										if (jo.getString("addr")
												.equals(extras
														.getString(BleService.EXTRA_ADDR))) {
											id = jo.getString("id");
										}

									}

									JSONArray curArray = null;
									try {
										curArray = data.getJSONArray(id);
									} catch (Exception e) {

									}

									if (curArray == null) {
										curArray = new JSONArray();
									}
									curArray.put(obj);
									data.put(id, curArray);
									// try {
									// Log.e("eeee", data.getJSONArray("head")
									// .length()
									// + "="
									// + data.getJSONArray("knee")
									// .length()
									// + "="
									// + data.getJSONArray("font")
									// .length() + "=");
									// } catch (Exception e) {
									//
									// }

								} else {
									data = new JSONObject();
								}
							}
						} catch (JSONException e) {

							e.printStackTrace();
						}
					}
				} else if (BleService.BLE_CHARACTERISTIC_NOTIFICATION
						.equals(action)) {
					// / Log.e("ffff", "aaaa");
					makeText("Notification state changed!");
					Bundle extras = intent.getExtras();
					boolean mNotifyStarted = extras
							.getBoolean(BleService.EXTRA_VALUE);
					if (mNotifyStarted) {
						if (stopNotifyModuleContext != null) {
							JSONObject obj = new JSONObject();
							try {
								obj.put("status", "SUCCESS");
								stopNotifyModuleContext.success(obj, true);
							} catch (JSONException e) {

								e.printStackTrace();
							}
						}
					} else {

						makeText("Start Notify");
					}
				} else if (BleService.BLE_CHARACTERISTIC_INDICATION
						.equals(action)) {

					makeText("Indication state changed");
				} else if (BleService.BLE_CHARACTERISTIC_WRITE.equals(action)) {

					makeText("写入成功");
					if (writeModuleContext != null) {
						JSONObject obj = new JSONObject();
						try {
							obj.put("status", SUCCESS);
							writeModuleContext.success(obj, true);
						} catch (JSONException e) {

							e.printStackTrace();
						}
					}
				} else if (BleService.BLE_REQUEST_FAILED.equals(action)) {
					if (mc != null) {
						JSONObject obj = new JSONObject();
						try {
							obj.put("status", STOP);
							obj.put("msg",
									intent.getExtras().get("REQUEST").toString());
							obj.put("result",
									intent.getExtras().getInt("REASON"));
							Log.e("action",
									intent.getExtras().getString(
											BleService.EXTRA_ADDR)
											+ ":::" + mc);
							mc.success(obj, false);
						} catch (JSONException e) {

						}
					}
					makeText("请求写入失败");
				} else if (BleService.BLE_STATUS_ABNORMAL.equals(action)) {

					makeText(action);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	};

	private void stopScan() {
		if (mBle != null) {
			mBle.stopScan();
			if (uZModuleContext != null) {
				try {
					JSONObject jo = new JSONObject();
					jo.put("status", STOP);
					jo.put("msg", "搜索完成");
					uZModuleContext.success(jo, false);
				} catch (JSONException e) {
					e.printStackTrace();
				}
			}

		}
	}

	private void scanLeDevice(final boolean enable) {
		if (mBle == null) {
			return;
		}
		if (enable) {
			// Stops scanning after a pre-defined scan period.
			mHandler.postDelayed(new Runnable() {
				@Override
				public void run() {
					stopScan();
				}
			}, SCAN_PERIOD);
			if (mBle != null) {
				mBle.startScan();

			}
		} else {
			stopScan();
		}
	}

	private ArrayList<HashMap<String, Object>> displayGattServices(
			List<BleGattService> gattServices) {
		if (gattServices == null)
			return null;
		String uuid = null;
		ArrayList<HashMap<String, Object>> gattServiceData = new ArrayList<HashMap<String, Object>>();
		// Loops through available GATT Services.
		for (BleGattService gattService : gattServices) {
			HashMap<String, Object> currentServiceData = new HashMap<String, Object>();
			uuid = gattService.getUuid().toString()
					.toUpperCase(Locale.getDefault());

			currentServiceData.put(LIST_NAME, Utils.BLE_SERVICES
					.containsKey(uuid) ? Utils.BLE_SERVICES.get(uuid) : "未知设备");
			currentServiceData.put(LIST_UUID, uuid);
			ArrayList<HashMap<String, String>> gattCharacteristicGroupData = new ArrayList<HashMap<String, String>>();
			List<BleGattCharacteristic> gattCharacteristics = gattService
					.getCharacteristics();
			// Loops through available Characteristics.
			for (BleGattCharacteristic gattCharacteristic : gattCharacteristics) {
				HashMap<String, String> currentCharaData = new HashMap<String, String>();
				uuid = gattCharacteristic.getUuid().toString()
						.toUpperCase(Locale.getDefault());
				currentCharaData
						.put(LIST_NAME,
								Utils.BLE_CHARACTERISTICS.containsKey(uuid) ? Utils.BLE_CHARACTERISTICS
										.get(uuid) : "未知属性");
				currentCharaData.put(LIST_UUID, uuid);
				currentCharaData.put("rw", gattCharacteristic.getProperties()
						+ "");
				gattCharacteristicGroupData.add(currentCharaData);
			}
			currentServiceData.put(CharaData, gattCharacteristicGroupData);
			gattServiceData.add(currentServiceData);
		}
		return gattServiceData;
	}

	/**
	 * 查找设备
	 * 
	 * @param moduleContext
	 */
	@UzJavascriptMethod
	public void jsmethod_scan(final UZModuleContext moduleContext) {
		BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();

		if (mAdapter.isEnabled()) {
			makeText("开始查找设备");
			uZModuleContext = moduleContext;
			mc = moduleContext;
			scanLeDevice(true);
		} else {
			// 提示开启蓝牙
			mAdapter.enable();
			try {
				JSONObject j = new JSONObject();
				j.put("status", STOP);
				j.put("msg", "蓝牙模块已开启,请重新搜索");
				moduleContext.success(j, false);
			} catch (Exception e) {
			}

		}

	}

	/**
	 * 停止查找设备
	 * 
	 * @param moduleContext
	 */
	@UzJavascriptMethod
	public void jsmethod_stop(final UZModuleContext moduleContext) {
		makeText("停止查找设备");
		uZModuleContext = moduleContext;
		mc = moduleContext;
		scanLeDevice(false);
	}

	/**
	 * 连接设备
	 * 
	 * @param moduleContext
	 */
	@UzJavascriptMethod
	public void jsmethod_connect(final UZModuleContext moduleContext) {
		devices = moduleContext.optJSONArray("devices");
		showToast = moduleContext.optBoolean("showToast");

		makeText("连接设备");
		connectedModuleContext = moduleContext;
		mc = moduleContext;
		if (mBle != null) {
			for (int i = 0; i < devices.length(); i++) {
				try {
					boolean b = mBle.requestConnect(devices.getJSONObject(i)
							.getString("addr"));
					System.out.println(b);
				} catch (JSONException e) {
					try {
						JSONObject obj = new JSONObject();
						obj.put("status", STOP);
						obj.put("addr",
								devices.getJSONObject(i).getString("addr"));
						obj.put("msg", e.getMessage());
						connectedModuleContext.success(obj, false);
					} catch (Exception e2) {

					}

				}
			}

		}
	}

	/**
	 * 关闭连接设备
	 * 
	 * @param moduleContext
	 */
	@UzJavascriptMethod
	public void jsmethod_stopConnect(final UZModuleContext moduleContext) {

		makeText("关闭连接设备");
		disConnectedModuleContext = moduleContext;
		mc = moduleContext;
		if (mBle != null) {
			for (int i = 0; i < devices.length(); i++) {
				try {
					mBle.disconnect(devices.getJSONObject(i).getString("addr"));
				} catch (JSONException e) {
					Log.e("xx", "aaaaaaaaaaaaaa");
					e.printStackTrace();
				}
			}

		}
	}

	/**
	 * 读取设备信息
	 * 
	 * @param moduleContext
	 */
	@UzJavascriptMethod
	public void jsmethod_read(final UZModuleContext moduleContext) {

		String service_uuid = moduleContext.optString("uuid");
		String characteristic_uuid = moduleContext.optString("charUuid");

		makeText("读取设备信息");
		readModuleContext = moduleContext;
		mc = moduleContext;
		if (mBle != null) {
			for (int i = 0; i < devices.length(); i++) {
				JSONObject jo = null;
				try {
					jo = devices.getJSONObject(i);
					mCharacteristic = mBle.getService(jo.getString("addr"),
							UUID.fromString(service_uuid)).getCharacteristic(
							UUID.fromString(characteristic_uuid));
					mBle.requestReadCharacteristic(jo.getString("addr"),
							mCharacteristic);
				} catch (Exception e) {
					try {
						JSONObject errjo = new JSONObject();
						errjo.put("status", STOP);
						errjo.put("msg", e.getMessage());
						readModuleContext.success(errjo, false);

					} catch (JSONException e1) {
						e1.printStackTrace();
					}

				}
			}

		}
	}

	/**
	 * 更新设备信息
	 * 
	 * @param moduleContext
	 */
	@UzJavascriptMethod
	public void jsmethod_notify(final UZModuleContext moduleContext) {
		String uuid = moduleContext.optString("uuid");
		String charUuid = moduleContext.optString("charUuid");
		notifyNow = moduleContext.optBoolean("notifyNow", false);

		makeText("更新设备信息");
		notifyModuleContext = moduleContext;
		mc = moduleContext;
		if (mBle != null) {

			for (int i = 0; i < devices.length(); i++) {
				JSONObject jo = null;
				try {
					jo = devices.getJSONObject(i);
					try {
						mCharacteristic = mBle.getService(jo.getString("addr"),
								UUID.fromString(uuid)).getCharacteristic(
								UUID.fromString(charUuid));
						mBle.requestCharacteristicNotification(
								jo.getString("addr"), mCharacteristic);
					} catch (Exception e) {
						JSONObject errjo = new JSONObject();
						errjo.put("status", 1);
						errjo.put("device", jo);
						notifyModuleContext.success(errjo, false);
					}

				} catch (JSONException e1) {

					e1.printStackTrace();
				}

			}
		}
	}

	/**
	 * 停止更新设备信息
	 * 
	 * @param moduleContext
	 */
	@UzJavascriptMethod
	public void jsmethod_stopNotify(final UZModuleContext moduleContext) {
		String uuid = moduleContext.optString("uuid");
		String charUuid = moduleContext.optString("charUuid");
		makeText("停止更新设备信息");
		// stopNotifyModuleContext = moduleContext;mc=moduleContext;
		try {
			if (mBle != null) {
				for (int i = 0; i < devices.length(); i++) {

					try {
						JSONObject jo = devices.getJSONObject(i);

						mCharacteristic = mBle.getService(jo.getString("addr"),
								UUID.fromString(uuid)).getCharacteristic(
								UUID.fromString(charUuid));
						mBle.requestStopNotification(jo.getString("addr"),
								mCharacteristic);

					} catch (Exception e) {

					}

				}
			}
			if (!notifyNow) {
				if (listConnStatus.size() == devices.length()) {
					if (data.keys().hasNext()) {
						data.put("status", SUCCESS);

					} else {
						data.put("status", STOP);
						data.put("msg", "请先进行notify操作");
					}
					moduleContext.success(data, false);
					data = new JSONObject();
				} else {
					// if (devices.length() != 1) {
					data.put("status", STOP);
					data.put("msg", "有一个或多个设备连接已断开");
					moduleContext.success(data, false);
					// }
				}
			} else {
				data.put("status", SUCCESS);
				moduleContext.success(data, false);
			}
		} catch (Exception e) {
			try {
				data.put("status", STOP);
				data.put("msg", "有一个或多个设备连接已断开");
				moduleContext.success(data, false);
			} catch (JSONException e1) {
				e1.printStackTrace();
			}

		}

	}

	/**
	 * 写入设备信息
	 * 
	 * @param moduleContext
	 */
	@UzJavascriptMethod
	public void jsmethod_write(final UZModuleContext moduleContext) {

		String service_uuid = moduleContext.optString("uuid");
		String characteristic_uuid = moduleContext.optString("charUuid");
		String writeContent = moduleContext.optString("writeContent");
		writeModuleContext = moduleContext;
		mc = moduleContext;

		makeText("写入设备信息");
		if (mBle != null) {
			for (int i = 0; i < devices.length(); i++) {
				JSONObject jo = null;
				try {
					jo = devices.getJSONObject(i);
					mCharacteristic = mBle.getService(jo.getString("addr"),
							UUID.fromString(service_uuid)).getCharacteristic(
							UUID.fromString(characteristic_uuid));
					byte[] inputData = Hex
							.decodeHex(writeContent.toCharArray());
					mCharacteristic.setValue(inputData);
					mBle.requestWriteCharacteristic(jo.getString("addr"),
							mCharacteristic, "");
				} catch (Exception e) {
					try {
						JSONObject errjo = new JSONObject();
						errjo.put("status", STOP);
						errjo.put("msg", e.getMessage());
						writeModuleContext.success(errjo, false);

					} catch (JSONException e1) {
						e1.printStackTrace();
					}

				}
			}

		}
	}

	@Override
	protected void onClean() {
		Log.e("xxzzz", "onClean");
		try {
			getContext().unregisterReceiver(mBleReceiver);
			scanLeDevice(false);
		} catch (Exception e) {
		}

	}

	private void makeText(String msg) {
		if (showToast) {
			Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT).show();
		}
	}

}
